package com.zjun.demo.experimentaldemo.honeycomb

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PointF
import android.graphics.SweepGradient
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.annotation.Size
import com.zjun.demo.experimentaldemo.R
import kotlin.math.cos
import kotlin.math.sin


/**
 * 蜂窝颜色选择器
 */
class HoneycombColorView : View {

    /**
     * 颜色数组
     */
    private var mShaderColor:IntArray = intArrayOf(0xFFFA2E2E.toInt(), 0xFFF4AD32.toInt(), 0xFF43D134.toInt(), 0xFF3FBBFD.toInt(), 0xFFAD30F6.toInt(), 0xFFEB2626.toInt())


    /**
     * 是否支持使用多种颜色的选择环
     */
    private var mSupportShaderColor:Boolean = true

    /**
     * 每个六边形的点的集合
     */
    private var mPolygonLatLons: MutableList<MutableList<ColorInfo>> = mutableListOf()

    private var mTouchX = 0f

    private var mTouchY = 0f

    /**
     * 颜色选择回调
     */
    var colorCallback: ColorCallback? = null

    /**
     * 颜色分组
     */
    private var mColors = mutableListOf<List<HoneycombColorEntity>>(
        mutableListOf(
            HoneycombColorEntity("#00FF74",true),
            HoneycombColorEntity("#1CFEF7"),
            HoneycombColorEntity("#43FFEF"),
        ),
        mutableListOf(
            HoneycombColorEntity("#32FF82"),
            HoneycombColorEntity("#7EFEF4"),
            HoneycombColorEntity("#C0FFF2"),
            HoneycombColorEntity("#FF733F"),
        ),
        mutableListOf(
            HoneycombColorEntity("#A0FF6C"),
            HoneycombColorEntity("#71FF56"),
            HoneycombColorEntity("#FFFFFF"),
            HoneycombColorEntity("#FE9455"),
            HoneycombColorEntity("#FE4C34"),
        ),
        mutableListOf(
            HoneycombColorEntity("#3F51FF"),
            HoneycombColorEntity("#8A95FF"),
            HoneycombColorEntity("#FEE6FE"),
            HoneycombColorEntity("#EDDA99"),
            HoneycombColorEntity("#FFBF20"),
            HoneycombColorEntity("#FE2F15"),
        ),
        mutableListOf(
            HoneycombColorEntity("#6E7FFE"),
            HoneycombColorEntity("#C3B9FE"),
            HoneycombColorEntity("#CFACFE"),
            HoneycombColorEntity("#FFA0CC"),
            HoneycombColorEntity("#FF64DB"),
        ),
        mutableListOf(
            HoneycombColorEntity("#BD5FFE"),
            HoneycombColorEntity("#BD5FFE"),
            HoneycombColorEntity("#FE81D6"),
            HoneycombColorEntity("#FE40DA"),
        ),
        mutableListOf(
            HoneycombColorEntity("#B139FE"),
            HoneycombColorEntity("#8906FF"),
            HoneycombColorEntity("#FE0EDC"),
        ),
    )

    /**
     * 不同列六边形水平间隔
     */
    private var mHorizontalInterval: Int = 0

    /**
     * 不同列六边形垂直间隔
     */
    private var mVerticalInterval: Int = 0

    /**
     * 六边形边对边的间隔距离
     */
    private var mInterval: Int = 0

    private var mOutLineInterval: Float = 0f

    /**
     * 描边颜色
     */
    private var mStrokeColor: Int = Color.WHITE

    private var mStrokeInnerColor: Int = Color.WHITE

    /**
     * 描边宽度
     */
    private var mStrokeWidth: Int = 0

    private var mStrokeInnerWidth: Float = 0f

    private var mIsOutLine: Boolean = false

    /**
     * 正六边形的边长
     */
    private var mEdgeLength: Int = 5

    /**
     * 六边形画笔
     */
    private val mPaint: Paint = Paint()

    /**
     * 描边画笔
     */
    private val mStrokePaint: Paint = Paint()

    private val mStrokeInnerPaint: Paint = Paint()

    private val path: Path = Path()

    private val mStrokePath: Path = Path()

    private val mStrokePathInner: Path = Path()

    constructor(context: Context) : super(context) {
        initialization()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initialization(attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initialization(attrs)
    }

    private fun initialization(attrs: AttributeSet? = null) {
        attrs?.run {
            val typedArray = context.obtainStyledAttributes(attrs, R.styleable.HoneycombColorView)
            typedArray.let {
                mInterval = it.getDimensionPixelOffset(R.styleable.HoneycombColorView_hc_interval, dip2px(3f).toInt())
                mStrokeColor = it.getColor(R.styleable.HoneycombColorView_hc_stroke_color, Color.WHITE)
                mStrokeInnerColor = it.getColor(R.styleable.HoneycombColorView_hc_stroke_inner_color, Color.WHITE)
                mStrokeWidth = it.getDimensionPixelOffset(R.styleable.HoneycombColorView_hc_stroke_width, dip2px(3f).toInt())
                mStrokeInnerWidth = it.getDimension(R.styleable.HoneycombColorView_hc_stroke_inner_width, dip2px(1.5f))
                mIsOutLine = it.getBoolean(R.styleable.HoneycombColorView_hc_is_out_line, false)
                mOutLineInterval = it.getDimension(R.styleable.HoneycombColorView_hc_is_out_line_interval, dip2px(3f))
            }
            typedArray.recycle()
        }
        if (attrs == null) {
            mInterval = dip2px(5f).toInt()
            mStrokeColor = Color.WHITE
            mStrokeInnerColor = Color.WHITE
            mStrokeWidth = dip2px(2f).toInt()
            mStrokeInnerWidth = dip2px(2f)
            mIsOutLine = false
            mOutLineInterval = dip2px(2f)
        }
        calculateHorizontalInterval()
        calculateVerticalInterval()
        mPaint.style = Paint.Style.FILL
        mPaint.isAntiAlias = true

        mStrokePaint.run {
            strokeWidth = mStrokeWidth.toFloat()
            isAntiAlias = true
            style = Paint.Style.STROKE
            shader = SweepGradient(
                0f,
                0f,
                intArrayOf(0xFFFA2E2E.toInt(), 0xFFF4AD32.toInt(), 0xFF43D134.toInt(), 0xFF3FBBFD.toInt(), 0xFFAD30F6.toInt(), 0xFFEB2626.toInt()),
                null
            )
        }

        mStrokeInnerPaint.run {
            strokeWidth = mStrokeInnerWidth
            isAntiAlias = true
            style = Paint.Style.STROKE
            color = mStrokeInnerColor
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val modeWidth = MeasureSpec.getMode(widthMeasureSpec)
        val modeHeight = MeasureSpec.getMode(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        var newWidthSize: Int
        var newHeightSize: Int
        if (modeWidth == MeasureSpec.EXACTLY) {
            newWidthSize = widthSize
            val estimateEdgeLengthBaseWidth = calculateEdgeLength(width = newWidthSize, isHorizontal = true)
            if (modeHeight == MeasureSpec.EXACTLY) {
                newHeightSize = heightSize
                val estimateHigh = calculateHigh(estimateEdgeLengthBaseWidth)
                mEdgeLength = if (estimateHigh > newHeightSize) {
                    calculateEdgeLength(height = newHeightSize, isHorizontal = false)
                } else {
                    calculateEdgeLength(width = newWidthSize, isHorizontal = true)
                }
            } else if (modeHeight == MeasureSpec.AT_MOST) {
                val estimateHigh = calculateHigh(estimateEdgeLengthBaseWidth)
                if (estimateHigh > heightSize) {
                    newHeightSize = heightSize
                    mEdgeLength = calculateEdgeLength(height = heightSize, isHorizontal = false)
                } else {
                    newHeightSize = estimateHigh
                    mEdgeLength = calculateEdgeLength(width = newWidthSize, isHorizontal = true)
                }
                setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(newHeightSize, MeasureSpec.EXACTLY))
            } else {
                mEdgeLength = estimateEdgeLengthBaseWidth
                newHeightSize = calculateHigh(estimateEdgeLengthBaseWidth)
                setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(newHeightSize, MeasureSpec.EXACTLY))
            }
        } else if (modeWidth == MeasureSpec.AT_MOST) {
            val estimateEdgeLengthBaseWidth = calculateEdgeLength(width = widthSize, isHorizontal = true)
            val estimateEdgeLengthBaseHeight = calculateEdgeLength(width = heightSize, isHorizontal = false)
            val estimateWidthSize = calculateWidth(estimateEdgeLengthBaseWidth)
            if (estimateWidthSize > widthSize) {

            }
            val estimateHeightSize = calculateHigh(estimateEdgeLengthBaseWidth)
        } else {

        }

        if (modeHeight == MeasureSpec.EXACTLY) {

        } else if (modeHeight == MeasureSpec.AT_MOST) {

        } else {

        }
    }

    /**
     * 更新颜色数组
     */
    fun updateSelectedColor(colorStr:String?) {
        for(colors in mColors){
            for(color in colors){
                color.isSelected = color.color == colorStr
            }
        }
        invalidate()
    }

    /**
     * 获取现在的颜色,null表示没有选中任何颜色
     */
    fun getSelectedColor():ColorInfo? {
        for(indexOut in 0 until mColors.size){
            for(indexInner in 0 until mColors[indexOut].size){
                if(mColors[indexOut][indexInner].isSelected){
                    return ColorInfo(-1f,-1f,mColors[indexOut][indexInner].color,indexOut,indexInner)
                }
            }
        }
        return null
    }

    /**
     * 更新颜色数组
     */
    fun updateColors(colors: MutableList<List<HoneycombColorEntity>>) {
        mColors = colors
        requestLayout()
    }

    /**
     * 更新颜色
     */
    fun updateStrokeColor(color: String) {
        mStrokeColor = Color.parseColor(color)
        mSupportShaderColor = false
        invalidate()
    }

    /**
     * 更新颜色数组，类似
     * intArrayOf(
     *                     Color.parseColor("#00FF74"),
     *                     Color.parseColor("#1CFEF7"),
     *                     Color.parseColor("#43FFEF"),
     *                     Color.parseColor("#32FF82"),
     *                     Color.parseColor("#7EFEF4"),
     *                     Color.parseColor("#C0FFF2"),
     *)
     */
    fun updateStrokeColors(colors: IntArray) {
        mShaderColor = colors
        mSupportShaderColor = true
        invalidate()
    }



    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        drawHexagons(canvas)
    }

    /**
     * 绘制六边形
     */
    private fun drawHexagons(canvas: Canvas) {
        mPolygonLatLons.clear()
        val totalWidth = calculateWidth(mEdgeLength, false)
        //x为第一列左角的圆心
        val x = (width - totalWidth) / 2 + mEdgeLength
        for (indexOut in 0 until mColors.size) {
            //y为第一列左角的圆心
            val y = (height - calculateVerticalTotalHexagonalHeight(mEdgeLength, mColors[indexOut].size)) / 2 + calculateSingleHexagonalHeight(mEdgeLength) / 2
            val circleX = x + mHorizontalInterval * indexOut + mEdgeLength * indexOut + mEdgeLength / 2 * indexOut
            for (indexInner in 0 until mColors[indexOut].size) {
                val circleY = y + mInterval * indexInner + calculateSingleHexagonalHeight(mEdgeLength) * indexInner
                mPaint.run {
                    setColorProxy(this, mColors[indexOut][indexInner].color)
                    this.strokeWidth = dip2px(1f)
                }
                path.reset()
                mStrokePath.reset()
                mStrokePathInner.reset()
                val points = mutableListOf<ColorInfo>()
                for (i in 0 until 6) {
                    val angle = i * 60 + 0.0
                    val cosineValue = cos(Math.toRadians(angle))
                    val sinValue = sin(Math.toRadians(angle))
                    val x1 = circleX + cosineValue * mEdgeLength
                    val y1 = circleY - sinValue * mEdgeLength

                    val strokeX1:Double
                    val strokeY1:Double
                    val strokeInnerX:Double
                    val strokeInnerY:Double

                    if(mIsOutLine){
                        strokeX1 = circleX + cosineValue * (mEdgeLength + mOutLineInterval - mStrokePaint.strokeWidth / 2)
                        strokeY1 = circleY - sinValue * (mEdgeLength + mOutLineInterval- mStrokePaint.strokeWidth / 2)
                        strokeInnerX = circleX + cosineValue * (mEdgeLength + mOutLineInterval - mStrokePaint.strokeWidth - mStrokeInnerPaint.strokeWidth/2)
                        strokeInnerY = circleY - sinValue * (mEdgeLength + mOutLineInterval - mStrokePaint.strokeWidth - mStrokeInnerPaint.strokeWidth/2)
                    }else{
                         strokeX1 = circleX + cosineValue * (mEdgeLength - mStrokePaint.strokeWidth / 2)
                         strokeY1 = circleY - sinValue * (mEdgeLength - mStrokePaint.strokeWidth / 2)
                         strokeInnerX = circleX + cosineValue * (mEdgeLength - mStrokePaint.strokeWidth - mStrokeInnerPaint.strokeWidth/2)
                         strokeInnerY = circleY - sinValue * (mEdgeLength - mStrokePaint.strokeWidth - mStrokeInnerPaint.strokeWidth/2)
                    }

                    if (i == 0) {
                        path.moveTo(x1.toFloat(), y1.toFloat())
                        mStrokePath.moveTo(strokeX1.toFloat(), strokeY1.toFloat())
                        mStrokePathInner.moveTo(strokeInnerX.toFloat(), strokeInnerY.toFloat())
                    } else {
                        path.lineTo(x1.toFloat(), y1.toFloat())
                        mStrokePath.lineTo(strokeX1.toFloat(), strokeY1.toFloat())
                        mStrokePathInner.lineTo(strokeInnerX.toFloat(), strokeInnerY.toFloat())
                    }
                    if(mSupportShaderColor){
                        mStrokePaint.run {
                            shader = SweepGradient(
                                x1.toFloat(),
                                y1.toFloat(),
                                mShaderColor,
                                null
                            )
                        }
                    }else{
                        mStrokePaint.run {
                            color = mStrokeColor
                            shader = null
                        }
                    }
                    points.add(
                        ColorInfo(
                            x1.toFloat(),
                            y1.toFloat(),
                            color = mColors[indexOut][indexInner].color,
                            cloumnIndex = indexOut,
                            rowIndex = indexInner,
                        )
                    )
                }
                mPolygonLatLons.add(points)
                path.close()
                mStrokePath.close()
                mStrokePathInner.close()
                canvas.drawPath(path, mPaint)
                if (mColors[indexOut][indexInner].isSelected) {
                    canvas.drawPath(mStrokePath, mStrokePaint)
                    canvas.drawPath(mStrokePathInner, mStrokeInnerPaint)
                }
            }
        }
    }


    /**
     * https://blog.csdn.net/qq_21460781/article/details/130257246
     * 修复 Android 10以上(含) paint设置颜色值崩溃
     */
    private fun setColorProxy(paint: Paint, @Size(min = 1) colorString: String) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            paint.setColor(Color.pack(Color.parseColor(colorString)))
        } else {
            paint.color = Color.parseColor(colorString)
        }
    }

    /**
     * 计算六边形边长
     * @param isHorizontal true按控件宽度计算边长,false按控件高度计算边长
     * @param width isHorizontal为true时必传,控件宽度
     * @param height isHorizontal为false时必传,控件高度
     */
    private fun calculateEdgeLength(width: Int = 0, height: Int = 0, isHorizontal: Boolean): Int {
        return if (isHorizontal) {
            val totalEdge = width - mHorizontalInterval * (getHorizontalMaxCount() - 1) - paddingStart - paddingEnd
            val count = getHorizontalMaxCount() + 1 + getHorizontalMaxCount() * 2
            (totalEdge / count) * 2
        } else {
            val hexagonalHeight = (height - paddingTop - paddingBottom) / getVerticalMaxCount()
            val halfHexagonalHeight = hexagonalHeight / 2
            val angleInDegrees = 30.0
            val cosineValue = cos(Math.toRadians(angleInDegrees))
            (halfHexagonalHeight / cosineValue).toInt()
        }
    }

    /**
     * 计算控件宽度
     * 六边形边长*水平最大数量+左边距+右边距+水平间隔*(水平最大数量-1)
     */
    private fun calculateWidth(edgeLength: Int, isWithPadding: Boolean = true): Int {
        val horizontalMaxCount = getHorizontalMaxCount()
        return if (isWithPadding) {
            edgeLength * horizontalMaxCount + paddingStart + paddingEnd + edgeLength / 2 * (horizontalMaxCount + 1) + mHorizontalInterval * (horizontalMaxCount - 1)
        } else {
            edgeLength * horizontalMaxCount + edgeLength / 2 * (horizontalMaxCount + 1) + mHorizontalInterval * (horizontalMaxCount - 1)
        }
    }

    /**
     * 计算控件高度
     * 六边形高度*垂直最大数量+间隔*(垂直最大数量-1)+上边距+下边距
     */
    private fun calculateHigh(edgeLength: Int): Int {
        val maxVerticalCount = getVerticalMaxCount()
        //③计算正六边形高度
        val heightOfRegularHexagon = calculateSingleHexagonalHeight(edgeLength)
        return if(mIsOutLine){
            (heightOfRegularHexagon * maxVerticalCount + (maxVerticalCount - 1) * mInterval + paddingBottom + paddingTop) + mInterval*2
        }else{
            (heightOfRegularHexagon * maxVerticalCount + (maxVerticalCount - 1) * mInterval + paddingBottom + paddingTop)
        }
    }


    /**
     * 计算不同列六边形水平间隔
     */
    private fun calculateHorizontalInterval() {
        //①求cos30°的值
        val angleInDegrees = 30.0 // 以度为单位的角度
        val cosineValue = cos(Math.toRadians(angleInDegrees))
        //②计算邻边长度
        mHorizontalInterval = (cosineValue * mInterval).toInt()
    }

    /**
     * 计算不同列六边形垂直间隔
     */
    private fun calculateVerticalInterval() {
        //计算六边形的高度
        //①求sin30°的值
        val angleInDegrees = 30.0 // 以度为单位的角度
        val angleInRadians = Math.toRadians(angleInDegrees)
        val sinValue = sin(angleInRadians)
        //②计算对边长度
        mVerticalInterval = (sinValue * mInterval).toInt()
    }

    /**
     * 获取垂直方向的最大六边形数量
     */
    private fun getVerticalMaxCount(): Int {
        var maxSize = 0
        for (colors in mColors) {
            if (maxSize < colors.size) {
                maxSize = colors.size
            }
        }
        return maxSize
    }

    var colorInfo: ColorInfo? = null

    /**
     * 颜色选择
     */
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mTouchX = event.x
                mTouchY = event.y
                colorInfo = getTouchColorInfo(mTouchX, mTouchY)
                if (colorInfo != null) {
                    return true
                }
            }

            MotionEvent.ACTION_UP -> {
                val upX = event.x
                val upY = event.y
                val colorInfosdf = getTouchColorInfo(upX, upY)
                if (colorInfo != null && colorInfosdf != null && colorInfosdf.color == colorInfo!!.color) {
                    for (msdff in mColors) {
                        for (SDFDSF in msdff) {
                            SDFDSF.isSelected = false
                        }
                    }
                    mColors[colorInfo!!.cloumnIndex][colorInfo!!.rowIndex].isSelected = true
                    colorCallback?.onColorSelection(mColors[colorInfo!!.cloumnIndex][colorInfo!!.rowIndex].color)
                    invalidate()
                    return true
                }
            }
        }
        return super.onTouchEvent(event)
    }

    private fun getTouchColorInfo(rawX: Float, rawY: Float): ColorInfo? {
        for (sdf in mPolygonLatLons) {
            val colorInfo = PointInPolygon.isPointInsidePolygon(
                pointLat = rawX.toDouble(),
                pointLon = rawY.toDouble(),
                polygonLatLons = sdf.map {
                    PointF(
                        it.x, it.y
                    )
                }.toTypedArray()
            )
            if (colorInfo) {
                return sdf[0]
            }
        }
        return null
    }


    /**
     * 获取垂直方向的最大六边形数量
     *
     */
    private fun getHorizontalMaxCount(): Int = mColors.size


    /**
     * dp转px
     * @param dipValue dp值
     */
    private fun dip2px(dipValue: Float): Float {
        val scale: Float = context.resources.displayMetrics.density
        return dipValue * scale + 0.5f
    }

    /**
     * 计算一列六边形的总高度
     * @param edgeLength 六边形边长
     * @param colorQuantity 一列的颜色数量
     * @param interval 六边形垂直间隔
     */
    private fun calculateVerticalTotalHexagonalHeight(
        edgeLength: Int,
        colorQuantity: Int,
        interval: Int = mInterval,
    ): Int {
        return interval * (colorQuantity - 1) + calculateSingleHexagonalHeight(edgeLength) * colorQuantity
    }

    /**
     * 计算单个六边形的高度
     * @param edgeLength 六边形边长
     */
    private fun calculateSingleHexagonalHeight(edgeLength: Int): Int {
        //计算六边形的高度
        //①求sin60°的值
        val angleInDegrees = 60.0 // 以度为单位的角度
        val radians = Math.toRadians(angleInDegrees); // 转换为弧度
        val sin = sin(radians) // 计算正弦值
        //②计算对边长度
        return (sin * edgeLength * 2).toInt()
    }

    interface ColorCallback {
        fun onColorSelection(color: String)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        colorCallback = null
    }

    data class HoneycombColorEntity(val color: String, var isSelected: Boolean = false)

    data class ColorInfo(val x: Float, val y: Float, val color: String, val cloumnIndex: Int, val rowIndex: Int)

}